﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Linq;
using Weber.CS3280.Inventory.Model;

namespace Weber.CS3280.Inventory.Logic
{
	/// <summary>
	/// Maintain the state of an individual inventory item. Protects all 
	/// data from being modified without this object being aware of it.
	/// Also responsible for saving and loading state
	/// </summary>
	public class InventoryItemController : INotifyPropertyChanged
	{
		private InventoryItem _item;
		private bool _isDirty;

		public InventoryItemController()
			: this(new InventoryItem())
		{
			// Assume that the instance will be dirty for a new inventory item
			IsDirty = true;
		}

		public InventoryItemController(Data.InventoryConnectionContext context, int inventoryItemId)
		{
			_item = new Data.InventoryItem(context, inventoryItemId).ToModel();
		}

		internal InventoryItemController(InventoryItem item)
		{
			_item = item;
		}

		/// <summary>
		/// Defines a copy constructor to separate edit screens from listing screens so that 
		/// changes in an edit screen won't affect any other part of the program
		/// </summary>
		/// <param name="source">Source InventoryItemController to copy</param>
		private InventoryItemController(InventoryItemController source)
		{
			_item = new InventoryItem();
			_item.InventoryItemId = InventoryItemId;
			_item.ItemCategoryId = ItemCategoryId;
			_item.ItemCode = ItemCode;
			_item.Name = Name;
			_item.RetailPrice = RetailPrice;
			_item.Cost = Cost;
			_isDirty = source.IsDirty;
		}

		public int? InventoryItemId
		{
			get { return _item.InventoryItemId; }
			private set
			{
				_item.InventoryItemId = value;
				NotifyPropertyChanged("InventoryItemId");
			}
		}

		/// <summary>
		/// Gets or sets the inventory item's item code. Use the item
		/// code to pull items into the inventory
		/// </summary>
		public int ItemCode
		{
			get { return _item.ItemCode; }
			set
			{
				// Include a setter-guard clause in order to catch
				// values that are the same as the original value.
				// This is ONLY to prevent the IsDirty flag toggling
				// when it shouldn't
				if (ItemCode == value)
					return;

				_item.ItemCode = value;
				// Let any subscribers know that the ItemCode changed
				NotifyPropertyChanged("ItemCode");
				IsDirty = true;
			}
		}

		/// <summary>
		/// Gets or sets the Name of the inventory item
		/// </summary>
		public string Name
		{
			get { return _item.Name; }
			set
			{
				if (_item.Name == value)
					return;
				_item.Name = value;
				// Let any subscribers know that the Name changed
				NotifyPropertyChanged("Name");
				IsDirty = true;
			}
		}

		public int ItemCategoryId
		{
			get { return _item.ItemCategoryId; }
			set
			{
				if (_item.ItemCategoryId == value)
					return;
				_item.ItemCategoryId = value;
				// Let any subscribers know that the Category changed
				NotifyPropertyChanged("Category");
				IsDirty = true;
			}
		}

		public decimal RetailPrice
		{
			get { return _item.RetailPrice; }
			set
			{
				if (_item.RetailPrice == value)
					return;
				_item.RetailPrice = value;
				// Let any subscribers know that the RetailPrice changed
				NotifyPropertyChanged("RetailPrice");
				IsDirty = true;
			}
		}

		public decimal Cost
		{
			get { return _item.Cost; }
			set
			{
				if (_item.Cost == value)
					return;
				_item.Cost = value;
				// Let any subscribers know that the Cost changed
				NotifyPropertyChanged("Cost");
				IsDirty = true;
			}
		}

		public bool IsDirty
		{
			get { return _isDirty; }
			set
			{
				if (_isDirty == value)
					return;
				_isDirty = value;
				// Let any subscribers know that the Cost changed
				NotifyPropertyChanged("IsDirty");
			}
		}

		public InventoryItemController Clone()
		{
			return new InventoryItemController(this);
		}

		public void Save(Data.InventoryConnectionContext context)
		{
			if (!IsDirty)
				return;

			// Save the inventory item to the database
			Data.InventoryItem dataItem = new Data.InventoryItem(_item);

			// Save raises the DataEvent.DataChanged event. This can be watched in the inventory list model
			dataItem.Save(context);

			// if the item was new, update the internal tracking of the item ID
			if (!InventoryItemId.HasValue)
				InventoryItemId = dataItem.ID;
		}

		public event PropertyChangedEventHandler PropertyChanged;

		internal void NotifyPropertyChanged(string propertyName)
		{
			// Check to see if the event has subscribers. If it does not have
			// any subscribers, it will be null. Otherwise, we can call it
			// like any other method.
			if (PropertyChanged != null)
				PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
		}

		public static InventoryItemController[] GetItems(Data.InventoryConnectionContext context)
		{
			// Load all of the items out of the database
			return Data.InventoryItem.GetAllInventoryItems(context)
				.Select(i => new InventoryItemController(i))
				.ToArray();
		}
	}
}
